﻿using System;
using System.Collections.Generic;

namespace MSTR.IS.Util
{
    [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field, AllowMultiple = false)]
    public class EnumItemDescAttribute : Attribute
    {
        public string Desc { get; protected set; }

        public EnumItemDescAttribute(string description)
            : base()
        {
            Desc = description;
        }
    }

    /// <summary>
    /// Represents properties of an enumeration item.
    /// </summary>
    public struct EnumItem
    {
        public object Value;

        public Type EnumType
        {
            get { return null == Value ? null : Value.GetType(); }
        }

        public string Name;

        /// <summary>
        /// Description text(a custom attribute named <typeparamref name="EnumItemDescAttribute"/>) of enumeration item.
        /// </summary>
        public string Desc;
    }

    public static class EnumEx
    {
        /// <summary>
        /// Get description text(a custom attribute named <typeparamref name="EnumItemDescAttribute"/>) of enumeration item.
        /// <para>-- By item name.</para>
        /// </summary>
        /// <param name="enumType">Type of the enumeration.</param>
        /// <param name="enumItemName">Name of enumeration item(exact name, CASE-SENSITIVE).</param>
        /// <returns>Returns null if attribute was not found.</returns>
        /// <exception cref="ArgumentNullException" />
        public static string GetEnumItemDesc(Type enumType, string enumItemName)
        {
            if (null == enumType)
                throw new ArgumentNullException("enumType");

            var eiField = enumType.GetField(enumItemName);
            if (null == eiField)
                return null;

            var eiDescAttributes = eiField.GetCustomAttributes(typeof(EnumItemDescAttribute), false);
            if (null == eiDescAttributes || 1 != eiDescAttributes.Length)
                return null;

            var eiDescAttribute = eiDescAttributes[0] as EnumItemDescAttribute;
            if (null == eiDescAttribute)
                return null;

            return eiDescAttribute.Desc ?? string.Empty;
        }

        /// <summary>
        /// Get description text(a custom attribute named <typeparamref name="EnumItemDescAttribute"/>) of enumeration item.
        /// <para>-- By item value.</para>
        /// </summary>
        /// <param name="enumValue">Value of an enumeration item.</param>
        /// <returns>Returns null if attribute was not found.</returns>
        /// <exception cref="ArgumentNullException" />
        public static string GetEnumItemDesc(object enumValue)
        {
            if (null == enumValue)
                throw new ArgumentNullException("enumValue");

            var enumType = enumValue.GetType();
            var enumItemName = Enum.GetName(enumType, enumValue);
            return GetEnumItemDesc(enumType, enumItemName);
        }

        /// <summary>
        /// Get an instance of <typeparamref name="EnumItem"/> by item value.
        /// </summary>
        /// <param name="enumValue">Value of an enumeration item.</param>
        /// <param name="includeDesc">Whether to get description text of <typeparamref name="EnumItemDescAttribute"/>.</param>
        /// <exception cref="ArgumentNullException" />
        public static EnumItem GetEnumItem(object enumValue, bool includeDesc)
        {
            if (null == enumValue)
                throw new ArgumentNullException("value");

            var enumType = enumValue.GetType();

            var li = new EnumItem();
            li.Value = enumValue;
            li.Name = Enum.GetName(enumType, enumValue);
            li.Desc = includeDesc ? GetEnumItemDesc(enumType, li.Name) : null;

            return li;
        }

        /// <summary>
        /// Get a list of EnumItem by values of a specified enumeration.
        /// </summary>
        /// <param name="enumType">Type of the enumeration.</param>
        /// <param name="includeDesc">Whether to get description text of <typeparamref name="EnumItemDescAttribute"/>.</param>
        /// <exception cref="ArgumentNullException" />
        public static List<EnumItem> GetEnumItems(Type enumType, bool includeDesc)
        {
            if (null == enumType)
                throw new ArgumentNullException("enumType");

            var values = Enum.GetValues(enumType);
            if (null == values)
                return null;

            var allItems = new List<EnumItem>(values.Length);
            if (0 == values.Length)
                return allItems;

            foreach (var value in values)
            {
                var li = GetEnumItem(value, includeDesc);
                allItems.Add(li);
            }

            return allItems;
        }

        /// <summary>
        /// Get a list of EnumItem by values of a specified enumeration.
        /// </summary>
        /// <param name="enumType">Type of the enumeration.</param>
        public static List<EnumItem> GetEnumItems(Type enumType)
        {
            return GetEnumItems(enumType, true);
        }
    }
}
